'use client';

import { useState, useEffect, useCallback, useRef } from 'react';
import { useParams, useRouter } from 'next/navigation';
import { useAuth } from '@/lib/auth-context';
import { projectsApi, assetsApi, invitationsApi, foldersApi, Project, Asset, Invitation, TranscodeStatus, FolderNode } from '@/lib/api';
import { Avatar } from '@/components/ui/avatar';
import { AssetCard } from '@/components/ui/AssetCard';
import { FolderTree } from '@/components/folders/FolderTree';
import { ShareModal } from '@/components/share/ShareModal';
import { TranscodeTasksPanel } from '@/components/transcode/TranscodeTasksPanel';
import { useDropzone } from 'react-dropzone';

async function safeCopy(text: string): Promise<void> {
  if (typeof window === 'undefined') return;
  try {
    const cb = navigator.clipboard;
    if (cb && typeof cb.writeText === 'function') {
      await cb.writeText(text);
    } else {
      const el = document.createElement('textarea');
      el.value = text;
      el.style.cssText = 'position:fixed;top:-999px;left:-999px;opacity:0';
      document.body.appendChild(el);
      el.focus(); el.select();
      try { document.execCommand('copy'); } catch { /* ignore */ }
      document.body.removeChild(el);
    }
  } catch { /* ignore */ }
}

const ROLE_COLORS: Record<string, string> = {
  ADMIN:   'badge-danger',
  EDITOR:  'badge-brand',
  REVIEWER:'badge-muted',
  VIEWER:  'badge-subtle',
};

const ROLE_LABELS: Record<string, string> = {
  ADMIN:   'Admin',
  EDITOR:  'Editor',
  REVIEWER:'Reviewer',
  VIEWER:  'Viewer',
};

function formatGroupDate(d: Date): string {
  const now = new Date();
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  const yesterday = new Date(today.getTime() - 86400000);
  const videoDay = new Date(d.getFullYear(), d.getMonth(), d.getDate());
  if (videoDay.getTime() === today.getTime()) return 'Today';
  if (videoDay.getTime() === yesterday.getTime()) return 'Yesterday';
  return d.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' });
}

function groupByDay(assets: Asset[]): [string, Asset[]][] {
  const sorted = [...assets].sort(
    (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
  );
  const groups: Record<string, Asset[]> = {};
  for (const a of sorted) {
    const d = new Date(a.createdAt);
    const day = new Date(d.getFullYear(), d.getMonth(), d.getDate()).toISOString();
    if (!groups[day]) groups[day] = [];
    groups[day].push(a);
  }
  return Object.entries(groups);
}

/** Collect asset IDs DIRECTLY in a folder (not from subfolders) */
function collectAssetIds(folders: FolderNode[], targetId: string | null): Set<string> {
  const ids = new Set<string>();
  if (targetId === null) return ids; // "All Videos" — no filter

  function findTarget(f: FolderNode): FolderNode | null {
    if (f.id === targetId) return f;
    for (const c of f.children) { const r = findTarget(c); if (r) return r; }
    return null;
  }

  for (const f of folders) {
    const target = findTarget(f);
    if (target) { for (const id of target.assetIds) ids.add(id); break; }
  }
  return ids;
}

/** Get direct subfolders of a folder */
function getSubfolders(folders: FolderNode[], targetId: string | null): FolderNode[] {
  if (targetId === null) return folders; // root: show top-level folders

  function findTarget(f: FolderNode): FolderNode | null {
    if (f.id === targetId) return f;
    for (const c of f.children) { const r = findTarget(c); if (r) return r; }
    return null;
  }

  for (const f of folders) {
    const target = findTarget(f);
    if (target) return [...target.children].sort((a, b) => a.order - b.order || a.name.localeCompare(b.name));
  }
  return [];
}

/** Build a map of assetId -> single deepest folder name */
function buildAssetFolders(allFolders: FolderNode[]): Map<string, string> {
  const map = new Map<string, string>();
  const depthMap = new Map<string, number>();

  function search(f: FolderNode, depth: number): void {
    for (const id of f.assetIds) {
      const existingDepth = depthMap.get(id) ?? -1;
      if (depth > existingDepth) {
        map.set(id, f.name);
        depthMap.set(id, depth);
      }
    }
    for (const child of f.children) search(child, depth + 1);
  }
  for (const f of allFolders) search(f, 0);
  return map;
}

/** Get the folder name an asset belongs to (deepest only) */
function getAssetFolderNames(assetFolders: Map<string, string>, assetId: string): string[] {
  const name = assetFolders.get(assetId);
  return name ? [name] : [];
}

/** Returns a breadcrumb path of folder names for the selected folder */
function getBreadcrumb(folders: FolderNode[], targetId: string | null): string[] {
  if (targetId === null) return [];
  const path: string[] = [];
  function search(f: FolderNode, trail: string[]): boolean {
    if (f.id === targetId) { path.push(...trail, f.name); return true; }
    for (const child of f.children) {
      if (search(child, [...trail, f.name])) return true;
    }
    return false;
  }
  for (const f of folders) if (search(f, [])) break;
  return path;
}

export default function ProjectDetailPage() {
  const params = useParams();
  const projectId = params.projectId as string;
  const { user, token } = useAuth();
  const router = useRouter();

  const [project, setProject] = useState<Project | null>(null);
  const [members, setMembers] = useState<any[]>([]);
  const [pendingInvites, setPendingInvites] = useState<Invitation[]>([]);
  const [assets, setAssets] = useState<Asset[]>([]);
  const [folders, setFolders] = useState<FolderNode[]>([]);
  const [allFolders, setAllFolders] = useState<FolderNode[]>([]);
  const [selectedFolderId, setSelectedFolderId] = useState<string | null>(null);
  const [viewMode, setViewMode] = useState<'file' | 'timeline'>('file');
  const [loading, setLoading] = useState(true);
  const [uploading, setUploading] = useState(false);
  const [sharingAssetId, setSharingAssetId] = useState<string | null>(null);
  const [activeTab, setActiveTab] = useState<'videos' | 'members' | 'transcode'>('videos');

  // Invite form state (single shared form)
  const [inviteEmail, setInviteEmail] = useState('');
  const [inviteRole, setInviteRole] = useState('REVIEWER');
  const [inviting, setInviting] = useState(false);
  const [inviteError, setInviteError] = useState('');
  const [inviteSuccess, setInviteSuccess] = useState('');
  const [createdLink, setCreatedLink] = useState('');
  const [createdLinkEmail, setCreatedLinkEmail] = useState('');
  const [linkCopiedAgain, setLinkCopiedAgain] = useState(false);

  // Edit member role
  const [editingRoleId, setEditingRoleId] = useState<string | null>(null);
  const [editingRole, setEditingRole] = useState('');
  const [updatingRole, setUpdatingRole] = useState(false);

  // Remove member
  const [confirmRemove, setConfirmRemove] = useState<{ id: string; name: string } | null>(null);
  const [removing, setRemoving] = useState(false);

  // Revoke invite
  const [revokingId, setRevokingId] = useState<string | null>(null);

  // Copy link
  const [copiedInviteId, setCopiedInviteId] = useState<string | null>(null);
  const [inviteUrlMap, setInviteUrlMap] = useState<Record<string, string>>({});

  const canManage = members.some(m =>
    m.user.id === user?.id && ['ADMIN', 'EDITOR'].includes(m.role)
  );
  const isOwner = project?.ownerId === user?.id;
  const isAdmin = members.some(m =>
    m.user.id === user?.id && m.role === 'ADMIN'
  );

  // ── Folder data derived from state ──────────────────────────────────────────
  // For file mode: only assets directly in the selected folder
  const folderAssetIds = assets.length > 0
    ? collectAssetIds(folders, selectedFolderId)
    : new Set<string>();
  // For timeline mode: assets in selected folder AND all its subfolders
  const timelineAssetIds = (() => {
    const ids = new Set<string>();
    if (selectedFolderId === null) return ids;
    function findTarget(f: FolderNode): FolderNode | null {
      if (f.id === selectedFolderId) return f;
      for (const c of f.children) { const r = findTarget(c); if (r) return r; }
      return null;
    }
    function collectAll(f: FolderNode): void {
      for (const id of f.assetIds) ids.add(id);
      for (const c of f.children) collectAll(c);
    }
    for (const f of folders) {
      const target = findTarget(f);
      if (target) { collectAll(target); break; }
    }
    return ids;
  })();
  const filteredAssets = selectedFolderId === null
    ? assets
    : (folderAssetIds.size > 0 ? assets.filter(a => folderAssetIds.has(a.id)) : []);
  // Timeline uses all assets in the selected folder AND its subfolders
  const timelineAssets = selectedFolderId === null
    ? assets
    : (timelineAssetIds.size > 0 ? assets.filter(a => timelineAssetIds.has(a.id)) : []);
  const subfolders = getSubfolders(folders, selectedFolderId);
  const breadcrumb = getBreadcrumb(folders, selectedFolderId);
  const assetFolders = buildAssetFolders(allFolders);

  // ── Delete project ──────────────────────────────────────────────────────────
  const [confirmDeleteProject, setConfirmDeleteProject] = useState(false);
  const [deletingProject, setDeletingProject] = useState(false);

  const handleDeleteProject = async () => {
    if (!token) return;
    setDeletingProject(true);
    try {
      await projectsApi.delete(token, projectId);
      router.push('/projects');
    } catch (err) {
      alert(err instanceof Error ? err.message : 'Failed to delete project');
    } finally {
      setDeletingProject(false);
      setConfirmDeleteProject(false);
    }
  };

  const loadFolders = useCallback(async () => {
    if (!token) return;
    try {
      const data = await foldersApi.list(token, projectId);
      setFolders(data.folders);
      setAllFolders(data.allFolders);
    } catch (e) {
      console.error('Failed to load folders:', e);
    }
  }, [token, projectId]);

  const loadAll = useCallback(async () => {
    if (!token) return;
    try {
      const [{ project: p }, { assets: a }] = await Promise.all([
        projectsApi.get(token, projectId),
        assetsApi.list(token, projectId),
      ]);
      setProject(p);
      setMembers(p.members ?? []);
      setAssets(a);

      if (canManage) {
        const { invitations } = await invitationsApi.list(token, projectId);
        setPendingInvites(invitations.filter((i: Invitation) => i.status === 'PENDING'));
      }
    } catch {
      router.push('/projects');
    } finally {
      setLoading(false);
    }
  }, [token, projectId, router, canManage]);

  useEffect(() => { loadAll(); }, [loadAll]);
  useEffect(() => { if (!loading && token) loadFolders(); }, [loading, token, loadFolders]);

  // ── Invite member ──────────────────────────────────────────────────────────
  const handleInvite = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!token || !inviteEmail.trim()) return;
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(inviteEmail.trim())) {
      setInviteError('Invalid email address');
      return;
    }
    setInviting(true);
    setInviteError('');
    setInviteSuccess('');
    setCreatedLink('');
    try {
      const { inviteUrl } = await invitationsApi.create(token, projectId, inviteEmail.trim(), inviteRole);
      const { invitations } = await invitationsApi.list(token, projectId);
      setPendingInvites(invitations.filter((i: Invitation) => i.status === 'PENDING'));
      setInviteUrlMap(prev => ({ ...prev, [inviteUrl.split('/').pop()!]: inviteUrl }));
      setInviteEmail('');
      setInviteSuccess(`Invitation sent to ${inviteEmail.trim()}`);
      setTimeout(() => setInviteSuccess(''), 3000);
    } catch (err) {
      setInviteError(err instanceof Error ? err.message : 'Failed to send invitation');
    } finally {
      setInviting(false);
    }
  };

  // ── Create & copy link ─────────────────────────────────────────────────────
  const handleCreateLink = async () => {
    if (!token || !inviteEmail.trim()) return;
    setInviting(true);
    setInviteError('');
    setInviteSuccess('');
    setCreatedLink('');
    setLinkCopiedAgain(false);
    const email = inviteEmail.trim();
    try {
      const { inviteUrl } = await invitationsApi.create(token, projectId, email, inviteRole);
      const { invitations } = await invitationsApi.list(token, projectId);
      setPendingInvites(invitations.filter((i: Invitation) => i.status === 'PENDING'));
      await safeCopy(inviteUrl);
      setCreatedLink(inviteUrl);
      setCreatedLinkEmail(email);
      setInviteEmail('');
    } catch (err: any) {
      const msg = err instanceof Error ? err.message : String(err);
      if (msg.includes('already exists') || msg.includes('already a member') || msg.includes('409')) {
        setInviteError(`An invitation for "${email}" is already pending or the user is already a member.`);
      } else {
        setInviteError(msg || 'Failed to create invitation link');
      }
    } finally {
      setInviting(false);
    }
  };

  // ── Change role ────────────────────────────────────────────────────────────
  const handleChangeRole = async (memberId: string) => {
    if (!token || !editingRole) return;
    setUpdatingRole(true);
    try {
      await projectsApi.updateMember(token, projectId, memberId, editingRole);
      setMembers(prev => prev.map(m => m.id === memberId ? { ...m, role: editingRole } : m));
      setEditingRoleId(null);
    } catch (err) {
      alert(err instanceof Error ? err.message : 'Failed to update role');
    } finally {
      setUpdatingRole(false);
    }
  };

  // ── Remove member ─────────────────────────────────────────────────────────
  const handleRemoveMember = async () => {
    if (!token || !confirmRemove) return;
    setRemoving(true);
    try {
      await projectsApi.removeMember(token, projectId, confirmRemove.id);
      setMembers(prev => prev.filter(m => m.id !== confirmRemove!.id));
      setConfirmRemove(null);
    } catch (err) {
      alert(err instanceof Error ? err.message : 'Failed to remove member');
    } finally {
      setRemoving(false);
    }
  };

  // ── Revoke invite ──────────────────────────────────────────────────────────
  const handleRevoke = async (invitationId: string) => {
    if (!token) return;
    setRevokingId(invitationId);
    try {
      await invitationsApi.revoke(token, invitationId);
      setPendingInvites(prev => prev.filter(i => i.id !== invitationId));
    } catch (err) {
      alert(err instanceof Error ? err.message : 'Failed to revoke invitation');
    } finally {
      setRevokingId(null);
    }
  };

  // ── Copy invite link ──────────────────────────────────────────────────────
  const handleCopyLink = async (invite: Invitation) => {
    const base = window.location.origin;
    const url = inviteUrlMap[invite.token] ?? `${base}/invite/${invite.token}`;
    await safeCopy(url);
    setCopiedInviteId(invite.id);
    setTimeout(() => setCopiedInviteId(null), 2000);
  };

  // ── Upload ─────────────────────────────────────────────────────────────────
  const handleDrop = async (acceptedFiles: File[]) => {
    if (!token || acceptedFiles.length === 0) return;
    setUploading(true);
    for (const file of acceptedFiles) {
      const formData = new FormData();
      formData.append('video', file);
      formData.append('projectId', projectId);
      formData.append('title', file.name.replace(/\.[^.]+$/, ''));
      try {
        const result = await assetsApi.upload(token, formData) as { asset: Asset };
        setAssets(prev => [result.asset, ...prev]);
      } catch (err) {
        console.error('Upload failed:', err);
        alert(`Upload failed: ${file.name}`);
      }
    }
    setUploading(false);
  };

  const { getRootProps: getUploadRootProps, getInputProps: getUploadInputProps, isDragActive: isUploadDragActive } = useDropzone({
    onDrop: handleDrop,
    accept: { 'video/*': ['.mp4', '.mov', '.webm', '.avi', '.mpeg'] },
    multiple: true,
    disabled: uploading,
  });

  // Poll for assets that are still processing
  const pollingRef = useRef<ReturnType<typeof setInterval> | null>(null);

  // ── Delete asset ─────────────────────────────────────────────────────────
  const [confirmDelete, setConfirmDelete] = useState<{ id: string; title: string } | null>(null);
  const [deletingId, setDeletingId] = useState<string | null>(null);

  const handleDeleteAsset = (id: string, title: string) => {
    setConfirmDelete({ id, title });
  };

  // ── Remove asset from a folder ──────────────────────────────────────────
  const handleRemoveFromFolder = useCallback(async (assetId: string, folderName: string) => {
    if (!token) return;
    // Find the folder by name within the project
    const folder = allFolders.find(f => f.name === folderName);
    if (!folder) return;
    try {
      await foldersApi.removeAsset(token, folder.id, assetId);
      // Refresh folder data so asset disappears from the folder
      loadFolders();
    } catch (err) {
      console.error('Failed to remove from folder:', err);
    }
  }, [token, allFolders, loadFolders]);

  const confirmDeleteAsset = async () => {
    if (!token || !confirmDelete) return;
    setDeletingId(confirmDelete.id);
    try {
      await assetsApi.delete(token, confirmDelete.id);
      setAssets(prev => prev.filter(a => a.id !== confirmDelete.id));
      setConfirmDelete(null);
    } catch (err) {
      alert(err instanceof Error ? err.message : 'Failed to delete video');
    } finally {
      setDeletingId(null);
    }
  };

  useEffect(() => {
    const processingAssets = assets.filter(a =>
      ['UPLOADING', 'PROCESSING', 'PENDING'].includes(a.transcodeStatus)
    );
    if (processingAssets.length === 0) {
      if (pollingRef.current) { clearInterval(pollingRef.current); pollingRef.current = null; }
      return;
    }
    if (pollingRef.current) return;

    pollingRef.current = setInterval(async () => {
      if (!token) return;
      try {
        const { assets: updated } = await assetsApi.list(token, projectId);
        setAssets(updated);
      } catch {}
    }, 3000);

    return () => { if (pollingRef.current) clearInterval(pollingRef.current); };
  }, [token, projectId, assets]);

  if (loading) {
    return (
      <div className="min-h-screen flex items-center justify-center" style={{ background: 'var(--bg)' }}>
        <div className="flex items-center gap-3" style={{ color: 'var(--text-muted)' }}>
          <div className="w-5 h-5 rounded-full animate-spin"
               style={{ borderColor: '#6366F1', borderTopColor: 'transparent' }} />
          <span className="text-sm">Loading…</span>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen" style={{ background: 'var(--bg)' }}>

      {/* Full-page upload overlay when dragging files */}
      {isUploadDragActive && (
        <div {...getUploadRootProps()} className="upload-drop-overlay">
          <input {...getUploadInputProps()} />
          <div className="text-center">
            <div className="w-16 h-16 rounded-2xl mx-auto mb-4 flex items-center justify-center"
                 style={{ background: 'rgba(99,102,241,0.15)', border: '2px solid rgba(99,102,241,0.4)' }}>
              <svg className="w-8 h-8" style={{ color: '#818CF8' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                <path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />
              </svg>
            </div>
            <p className="text-lg font-medium" style={{ color: 'var(--text)' }}>Drop videos to upload</p>
            <p className="text-sm mt-1" style={{ color: 'var(--text-muted)' }}>MP4, MOV, WebM — up to 500MB each</p>
          </div>
        </div>
      )}

      {/* Header */}
      <header className="sticky top-0 z-10 px-4 md:px-8 py-3 md:py-4 flex items-center gap-2 md:gap-4 shrink-0 flex-wrap"
              style={{
                background: 'rgba(10,11,20,0.80)',
                backdropFilter: 'blur(12px)',
                borderBottom: '1px solid rgba(255,255,255,0.06)',
              }}>
        <button
          onClick={() => router.push('/projects')}
          className="flex items-center gap-1.5 text-sm transition-colors shrink-0"
          style={{ color: 'var(--text-muted)' }}
        >
          <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
            <path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
          </svg>
          <span className="hidden sm:inline">Projects</span>
        </button>

        <div className="w-px h-4 hidden sm:block shrink-0" style={{ background: 'rgba(255,255,255,0.10)' }} />

        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2">
            <h1 className="text-sm font-semibold truncate" style={{ color: 'var(--text)' }}>
              {project?.name}
            </h1>
            {canManage && (
              <span className="text-[10px] px-1.5 py-0.5 rounded-full shrink-0"
                    style={{ background: 'rgba(99,102,241,0.12)', color: '#A5B4FC' }}>
                {isAdmin ? 'Owner' : 'Editor'}
              </span>
            )}
            {!canManage && !isAdmin && (
              <span className="text-[10px] px-1.5 py-0.5 rounded-full shrink-0"
                    style={{ background: 'rgba(255,255,255,0.04)', color: 'var(--text-subtle)' }}>
                {members.find(m => m.user.id === user?.id)?.role ?? 'Member'}
              </span>
            )}
          </div>
          {project?.description && (
            <p className="text-xs truncate mt-0.5 hidden sm:block" style={{ color: 'var(--text-muted)' }}>
              {project.description}
            </p>
          )}
        </div>

        {/* Upload button — compact, in header */}
        {canManage && (
          <button
            {...getUploadRootProps()}
            className="flex items-center gap-1.5 text-xs px-3 py-1.5 rounded-lg shrink-0 transition-all"
            style={{ background: 'rgba(99,102,241,0.12)', color: '#A5B4FC' }}
            title="Upload video"
          >
            <input {...getUploadInputProps()} />
            {uploading ? (
              <div className="w-3.5 h-3.5 rounded-full animate-spin" style={{ borderColor: '#A5B4FC', borderTopColor: 'transparent', borderWidth: '2px' }} />
            ) : (
              <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                <path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />
              </svg>
            )}
            <span className="hidden sm:inline">Upload</span>
          </button>
        )}

        {/* Tabs */}
        <div className="flex items-center gap-1 p-1 rounded-lg shrink-0"
             style={{ background: 'rgba(255,255,255,0.04)' }}>
          {[
            { tab: 'videos', label: 'Videos', count: assets.length },
            { tab: 'transcode', label: 'Tasks', count: assets.filter(a => a.transcodeStatus !== 'COMPLETED').length },
            { tab: 'members', label: 'Members', count: members.length },
          ].map(({ tab, label, count }) => (
            <button key={tab}
              onClick={() => setActiveTab(tab as any)}
              className="relative px-2 sm:px-3 py-1.5 rounded-md text-xs font-medium transition-all flex items-center gap-1.5 shrink-0"
              style={{
                background: activeTab === tab ? 'rgba(99,102,241,0.20)' : 'transparent',
                color: activeTab === tab ? '#A5B4FC' : 'var(--text-muted)',
              }}
              title={label}
            >
              {tab === 'videos' && (
                <svg className="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M15.75 10.5l4.72-4.72a.75.75 0 011.28.53v11.38a.75.75 0 01-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25h-9A2.25 2.25 0 002.25 7.5v9a2.25 2.25 0 002.25 2.25z" />
                </svg>
              )}
              {tab === 'transcode' && (
                <svg className="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M19.5 12c0-1.232-.046-2.453-.138-3.662a4.006 4.006 0 00-3.7-3.7 48.678 48.678 0 00-7.324 0 4.006 4.006 0 00-3.7 3.7c-.017.22-.032.441-.046.662M19.5 12l3-3m-3 3l-3-3m-12 3c0 1.232.046 2.453.138 3.662a4.006 4.006 0 003.7 3.7 48.656 48.656 0 007.324 0 4.006 4.006 0 003.7-3.7c.017-.22.032-.441.046-.662M4.5 12l3 3m-3-3l-3 3" />
                </svg>
              )}
              {tab === 'members' && (
                <svg className="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" />
                </svg>
              )}
              <span className="hidden sm:inline">{label}</span>
              <span className="text-[10px] px-1 py-0.5 rounded-full"
                    style={{
                      background: tab === 'transcode'
                        ? 'rgba(167,139,250,0.25)'
                        : 'rgba(255,255,255,0.06)',
                      color: tab === 'transcode' ? '#A78BFA' : 'inherit',
                    }}>
                {count}
              </span>
            </button>
          ))}
        </div>

        {/* Delete project — owner only */}
        {isOwner && (
          <button
            onClick={() => setConfirmDeleteProject(true)}
            className="flex items-center justify-center p-1.5 rounded-lg transition-all shrink-0"
            style={{ color: '#F87171', background: 'rgba(248,113,113,0.08)' }}
            title="Delete project"
          >
            <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
              <path strokeLinecap="round" strokeLinejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
            </svg>
          </button>
        )}
      </header>

      <div className="px-4 md:px-8 py-4 md:py-6">

        {/* ── Videos Tab ───────────────────────────────────────────────────── */}
        {activeTab === 'videos' && (
          <>
            {/* File/Timeline mode toggle + breadcrumb bar */}
            {activeTab === 'videos' && (
              <div className="flex items-center gap-3 mb-5 flex-wrap">
                {/* Breadcrumb */}
                <nav className="flex items-center gap-1 text-xs shrink min-w-0" style={{ color: 'var(--text-muted)' }}>
                  <span className="truncate">{project?.name}</span>
                  {breadcrumb.map((name, i) => (
                    <span key={i} className="flex items-center gap-1 shrink-0">
                      <svg className="w-3 h-3 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                        <path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
                      </svg>
                      <span className={i === breadcrumb.length - 1 ? '' : 'opacity-60'}>{name}</span>
                    </span>
                  ))}
                </nav>

                <div className="flex-1" />

                {/* Asset count */}
                <span className="text-xs px-2 py-1 rounded-full shrink-0"
                      style={{ background: 'rgba(255,255,255,0.05)', color: 'var(--text-muted)' }}>
                  {filteredAssets.length} video{filteredAssets.length !== 1 ? 's' : ''}
                </span>

                {/* Mode toggle */}
                <div className="flex items-center gap-0.5 p-0.5 rounded-lg shrink-0"
                     style={{ background: 'rgba(255,255,255,0.05)' }}>
                  {[
                    { mode: 'file' as const, label: 'File', icon: (
                      <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                        <path strokeLinecap="round" strokeLinejoin="round" d="M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-1.5A2.25 2.25 0 0115 9v.75m-8.69-6.44H5.5a2.25 2.25 0 00-2.25 2.25v.75h13.5v-.75a2.25 2.25 0 00-2.25-2.25H12" />
                      </svg>
                    )},
                    { mode: 'timeline' as const, label: 'Timeline', icon: (
                      <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                        <path strokeLinecap="round" strokeLinejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
                      </svg>
                    )},
                  ].map(({ mode, label, icon }) => (
                    <button key={mode}
                      onClick={() => setViewMode(mode)}
                      className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs font-medium transition-all whitespace-nowrap"
                      style={{
                        background: viewMode === mode ? 'rgba(99,102,241,0.20)' : 'transparent',
                        color: viewMode === mode ? '#A5B4FC' : 'var(--text-muted)',
                      }}
                    >
                      {icon}
                      <span className="hidden sm:inline">{label}</span>
                    </button>
                  ))}
                </div>
              </div>
            )}

            <div className="flex gap-5">
              {/* Left panel: FolderTree (both file and timeline modes) */}
              <aside className="w-52 shrink-0 hidden md:block">
                <FolderTree
                  folders={folders}
                  allFolders={allFolders}
                  selectedFolderId={selectedFolderId}
                  onSelectFolder={setSelectedFolderId}
                  canManage={canManage}
                  token={token ?? ''}
                  projectId={projectId}
                  onRefresh={loadFolders}
                  totalAssetCount={assets.length}
                />
              </aside>

              {/* Main content */}
              <div className="flex-1 min-w-0">

                {/* Upload zone for non-managers */}
                {!canManage && (
                  <div className="mb-6 rounded-2xl p-6 text-center animate-fade-in"
                       style={{ background: 'rgba(255,255,255,0.01)', border: '1px solid rgba(255,255,255,0.05)', borderRadius: '16px' }}>
                    <div className="w-10 h-10 rounded-2xl mx-auto mb-3 flex items-center justify-center"
                         style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.06)' }}>
                      <svg className="w-5 h-5" style={{ color: 'var(--text-subtle)' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                        <path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />
                      </svg>
                    </div>
                    <p className="text-xs" style={{ color: 'var(--text-muted)' }}>
                      Your role ({members.find(m => m.user.id === user?.id)?.role ?? 'Member'}) does not allow uploading.
                    </p>
                  </div>
                )}

                {/* File mode content */}
                {viewMode === 'file' && (filteredAssets.length === 0 && subfolders.length === 0) ? (
                  <div className="text-center py-16 rounded-2xl animate-fade-in"
                       style={{ background: 'rgba(255,255,255,0.02)', border: '1px solid rgba(255,255,255,0.06)' }}>
                    <div className="w-14 h-14 rounded-2xl mx-auto mb-4 flex items-center justify-center"
                         style={{ background: 'rgba(99,102,241,0.08)', border: '1px solid rgba(99,102,241,0.12)' }}>
                      <svg className="w-7 h-7" style={{ color: '#6366F1' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.2}>
                        <path strokeLinecap="round" strokeLinejoin="round" d="M15.75 10.5l4.72-4.72a.75.75 0 011.28.53v11.38a.75.75 0 01-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25h-9A2.25 2.25 0 002.25 7.5v9a2.25 2.25 0 002.25 2.25z" />
                      </svg>
                    </div>
                    <p className="text-sm font-medium mb-1" style={{ color: 'var(--text)' }}>
                      {selectedFolderId ? 'No videos in this folder' : 'No videos yet'}
                    </p>
                    <p className="text-xs" style={{ color: 'var(--text-muted)' }}>
                      {selectedFolderId
                        ? 'Drag videos here or move them from other folders'
                        : (canManage ? 'Upload your first video using the Upload button above' : 'Videos will appear here once uploaded')}
                    </p>
                  </div>
                ) : viewMode === 'file' ? (
                  // File mode: subfolders + videos
                  <div>
                    {/* Subfolders */}
                    {subfolders.length > 0 && (
                      <div className="mb-6">
                        <div className="flex items-center gap-3 mb-3">
                          <span className="text-xs font-medium" style={{ color: 'var(--text-subtle)' }}>Folders</span>
                          <div className="flex-1 h-px" style={{ background: 'rgba(255,255,255,0.05)' }} />
                        </div>
                        <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-6 gap-3">
                          {subfolders.map(folder => (
                            <button
                              key={folder.id}
                              onClick={() => setSelectedFolderId(folder.id)}
                              className="flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-left transition-all hover:brightness-110 group"
                              style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.06)' }}
                            >
                              <svg className="w-5 h-5 shrink-0" style={{ color: '#A78BFA' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9A2.25 2.25 0 0019.5 6.75h-1.5A2.25 2.25 0 0115.75 9v.75m-8.69-6.44H5.5a2.25 2.25 0 00-2.25 2.25v.75h13.5v-.75a2.25 2.25 0 00-2.25-2.25H12" />
                              </svg>
                              <div className="flex-1 min-w-0">
                                <div className="text-xs font-medium truncate" style={{ color: 'var(--text)' }}>{folder.name}</div>
                                {folder.assetCount > 0 && (
                                  <div className="text-[10px]" style={{ color: 'var(--text-subtle)' }}>{folder.assetCount} video{folder.assetCount !== 1 ? 's' : ''}</div>
                                )}
                              </div>
                              <svg className="w-3 h-3 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity" style={{ color: 'var(--text-subtle)' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
                              </svg>
                            </button>
                          ))}
                        </div>
                      </div>
                    )}

                    {/* Videos in this folder */}
                    {filteredAssets.length > 0 && (
                      <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
                        {filteredAssets.map((asset, i) => (
                          <AssetCard
                            key={asset.id}
                            asset={asset}
                            canManage={canManage}
                            showHour={false}
                            onPlay={() => router.push(`/review/${asset.id}`)}
                            onDelete={() => handleDeleteAsset(asset.id, asset.title)}
                            onCancel={async (id) => {
                              if (!token) return;
                              try {
                                await assetsApi.cancelTranscode(token, id);
                                setAssets(prev => prev.map(a => a.id === id ? { ...a, transcodeStatus: 'PENDING', transcodeProgress: 0, transcodeError: null, hlsPath: null, transcodePaused: false } : a));
                              } catch (err) { alert(err instanceof Error ? err.message : 'Failed to cancel transcode'); }
                            }}
                            onPause={async (id) => {
                              if (!token) return;
                              try { await assetsApi.pauseTranscode(token, id); setAssets(prev => prev.map(a => a.id === id ? { ...a, transcodePaused: true } : a)); }
                              catch (err) { alert(err instanceof Error ? err.message : 'Failed to pause transcode'); }
                            }}
                            onResume={async (id) => {
                              if (!token) return;
                              try { await assetsApi.resumeTranscode(token, id); setAssets(prev => prev.map(a => a.id === id ? { ...a, transcodePaused: false } : a)); }
                              catch (err) { alert(err instanceof Error ? err.message : 'Failed to resume transcode'); }
                            }}
                            animationDelay={i * 40}
                            folderNames={getAssetFolderNames(assetFolders, asset.id)}
                            onShare={setSharingAssetId}
                            isShared={!!asset.isShared}
                            onRemoveFromFolder={handleRemoveFromFolder}
                          />
                        ))}
                      </div>
                    )}
                  </div>
                ) : (
                  // Timeline mode: grouped by date
                  <div className="space-y-8">
                    {groupByDay(timelineAssets).map(([dayKey, dayAssets]) => {
                      const groupDate = new Date(dayKey);
                      const showHour = dayAssets.length > 1;
                      return (
                        <div key={dayKey}>
                          <div className="flex items-center gap-3 mb-4">
                            <span className="text-xs font-semibold shrink-0" style={{ color: 'var(--text-muted)' }}>
                              {formatGroupDate(groupDate)}
                            </span>
                            <div className="flex-1 h-px" style={{ background: 'rgba(255,255,255,0.06)' }} />
                            <span className="text-[10px] shrink-0" style={{ color: 'var(--text-subtle)' }}>
                              {dayAssets.length} video{dayAssets.length !== 1 ? 's' : ''}
                            </span>
                          </div>
                          <div className="space-y-3">
                            {dayAssets.map((asset, i) => {
                              const createdAt = new Date(asset.createdAt);
                              return (
                                <div key={asset.id}
                                  className="flex items-center gap-4 p-3 rounded-xl cursor-pointer group transition-colors animate-fade-in"
                                  style={{ background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.05)' }}
                                  onClick={() => router.push(`/review/${asset.id}`)}
                                  draggable={canManage}
                                  onDragStart={canManage ? (e) => {
                                    e.dataTransfer.setData('assetId', asset.id);
                                    e.dataTransfer.setData('text/plain', asset.title);
                                    e.dataTransfer.effectAllowed = 'move';
                                    if (asset.thumbnail && asset.transcodeStatus === 'COMPLETED') {
                                      const ghost = document.createElement('div');
                                      ghost.style.cssText = 'position:fixed;top:-9999px;left:-9999px;display:flex;align-items:center;gap:8px;padding:6px 10px;background:rgba(15,15,25,0.95);border:1px solid rgba(99,102,241,0.4);border-radius:8px;backdrop-filter:blur(8px);font-family:system-ui,sans-serif;z-index:99999;';
                                      const img = document.createElement('img');
                                      img.src = `/uploads/${asset.thumbnail}`;
                                      img.style.cssText = 'height:48px;border-radius:5px;object-fit:cover;';
                                      const label = document.createElement('span');
                                      label.textContent = asset.title;
                                      label.style.cssText = 'color:#e2e8f0;font-size:12px;font-weight:500;max-width:160px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;';
                                      ghost.appendChild(img);
                                      ghost.appendChild(label);
                                      document.body.appendChild(ghost);
                                      e.dataTransfer.setDragImage(ghost, 30, 28);
                                      setTimeout(() => document.body.removeChild(ghost), 0);
                                    }
                                  } : undefined}
                                >
                                  {/* Thumbnail */}
                                  <div className="w-24 sm:w-32 shrink-0 rounded-lg overflow-hidden aspect-video"
                                       style={{ background: '#080810' }}>
                                    {asset.thumbnail && asset.transcodeStatus === 'COMPLETED' ? (
                                      <img src={`/uploads/${asset.thumbnail}`} alt={asset.title} className="w-full h-full object-cover" style={{ opacity: 0.8 }} />
                                    ) : (
                                      <div className="w-full h-full flex items-center justify-center">
                                        <svg className="w-6 h-6" style={{ color: 'rgba(255,255,255,0.15)' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1}>
                                          <path strokeLinecap="round" strokeLinejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
                                          <path strokeLinecap="round" strokeLinejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                                        </svg>
                                      </div>
                                    )}
                                  </div>

                                  {/* Info */}
                                  <div className="flex-1 min-w-0">
                                    <div className="flex items-start justify-between gap-2 mb-1">
                                      <h3 className="text-sm font-medium truncate" style={{ color: 'var(--text)' }}>{asset.title}</h3>
                                      {asset.duration && (
                                        <span className="text-xs shrink-0 px-1.5 py-0.5 rounded font-mono"
                                              style={{ background: 'rgba(0,0,0,0.5)', color: '#E2E8F0' }}>
                                          {`${Math.floor(asset.duration / 60)}:${Math.floor(asset.duration % 60).toString().padStart(2, '0')}`}
                                        </span>
                                      )}
                                    </div>
                                    {/* Folder tags */}
                                    {(() => {
                                      const tags = getAssetFolderNames(assetFolders, asset.id);
                                      return tags.length > 0 ? (
                                        <div className="flex flex-wrap gap-1 mb-1">
                                          {tags.map((name, i) => (
                                            <span key={i} className="text-[10px] px-1.5 py-0.5 rounded"
                                                  style={{ background: 'rgba(99,102,241,0.12)', color: '#A5B4FC' }}>
                                              {name}
                                            </span>
                                          ))}
                                        </div>
                                      ) : null;
                                    })()}
                                    <div className="flex items-center gap-2 text-xs" style={{ color: 'var(--text-muted)' }}>
                                      <span className="truncate">{asset.uploader?.name ?? 'Unknown'}</span>
                                      <span>·</span>
                                      <span className="shrink-0 text-[10px]" style={{ color: 'var(--text-subtle)' }}>
                                        {showHour
                                          ? createdAt.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: true })
                                          : createdAt.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
                                      </span>
                                      <span>·</span>
                                      <span>{(asset as any)._count?.comments ?? 0} comments</span>
                                    </div>
                                  </div>

                                  {/* Play button */}
                                  <div className="w-8 h-8 rounded-full flex items-center justify-center shrink-0 opacity-0 group-hover:opacity-100 transition-opacity"
                                       style={{ background: 'rgba(99,102,241,0.20)', color: '#A5B4FC' }}>
                                    <svg className="w-4 h-4 ml-0.5" fill="currentColor" viewBox="0 0 24 24">
                                      <path d="M8 5v14l11-7z" />
                                    </svg>
                                  </div>
                                </div>
                              );
                            })}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                )}
              </div>
            </div>
          </>
        )}

        {/* ── Transcode Tasks Tab ─────────────────────────────────────────── */}
        {activeTab === 'transcode' && (
          <div className="animate-fade-in">
            <TranscodeTasksPanel
              assets={assets}
              token={token}
              canManage={canManage}
              onDelete={handleDeleteAsset}
              onCancel={async (id) => {
                if (!token) return;
                try {
                  await assetsApi.cancelTranscode(token, id);
                  setAssets(prev => prev.map(a => a.id === id ? { ...a, transcodeStatus: 'PENDING', transcodeProgress: 0, transcodeError: null, hlsPath: null, transcodePaused: false } : a));
                } catch (err) { alert(err instanceof Error ? err.message : 'Failed to cancel transcode'); }
              }}
              onPause={async (id) => {
                if (!token) return;
                try { await assetsApi.pauseTranscode(token, id); setAssets(prev => prev.map(a => a.id === id ? { ...a, transcodePaused: true } : a)); }
                catch (err) { alert(err instanceof Error ? err.message : 'Failed to pause transcode'); }
              }}
              onResume={async (id) => {
                if (!token) return;
                try { await assetsApi.resumeTranscode(token, id); setAssets(prev => prev.map(a => a.id === id ? { ...a, transcodePaused: false } : a)); }
                catch (err) { alert(err instanceof Error ? err.message : 'Failed to resume transcode'); }
              }}
              onReprocess={async (id) => {
                if (!token) return;
                try {
                  await assetsApi.cancelTranscode(token, id);
                  setAssets(prev => prev.map(a => a.id === id ? { ...a, transcodeStatus: 'PENDING', transcodeProgress: 0, transcodeError: null, hlsPath: null, transcodePaused: false } : a));
                } catch (err) { alert(err instanceof Error ? err.message : 'Failed to reprocess transcode'); }
              }}
            />
          </div>
        )}

        {/* ── Members Tab ─────────────────────────────────────────────────── */}
        {activeTab === 'members' && (
          <div className="max-w-3xl animate-fade-in">

            {/* Invite form */}
            {canManage && (
              <div className="card p-5 mb-6">
                <h2 className="text-sm font-semibold mb-4" style={{ color: 'var(--text)' }}>
                  Invite someone
                </h2>

                <div className="space-y-3">
                  <form
                    onSubmit={e => { e.preventDefault(); handleInvite(e); }}
                    className="flex items-end gap-3 flex-wrap"
                  >
                    <div className="flex-1 min-w-[180px]">
                      <label className="block text-xs mb-1.5" style={{ color: 'var(--text-muted)' }}>
                        Email address
                      </label>
                      <input
                        type="email"
                        className="input"
                        value={inviteEmail}
                        onChange={e => setInviteEmail(e.target.value)}
                        placeholder="colleague@company.com"
                      />
                    </div>
                    <div className="w-36">
                      <label className="block text-xs mb-1.5" style={{ color: 'var(--text-muted)' }}>Role</label>
                      <select
                        className="input"
                        value={inviteRole}
                        onChange={e => setInviteRole(e.target.value)}
                      >
                        {Object.entries(ROLE_LABELS).map(([value, label]) => (
                          <option key={value} value={value}>{label}</option>
                        ))}
                      </select>
                    </div>
                    <button
                      type="button"
                      disabled={inviting || !inviteEmail.trim()}
                      onClick={handleCreateLink}
                      className="btn btn-secondary btn-md"
                      title="Create invite link and copy to clipboard"
                    >
                      {inviting ? 'Creating…' : (
                        <span className="flex items-center gap-1.5">
                          <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                            <path strokeLinecap="round" strokeLinejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244" />
                          </svg>
                          Copy Link
                        </span>
                      )}
                    </button>
                    <button
                      type="submit"
                      disabled={inviting || !inviteEmail.trim()}
                      className="btn btn-primary btn-md"
                      title="Send invite"
                    >
                      {inviting ? 'Sending…' : 'Send Invite'}
                    </button>
                  </form>

                  {createdLink && (
                    <div className="rounded-lg p-4 animate-scale-in"
                         style={{ background: 'rgba(34,197,94,0.08)', border: '1px solid rgba(34,197,94,0.20)' }}>
                      <div className="flex items-center gap-2 mb-1.5">
                        <svg className="w-4 h-4 shrink-0" style={{ color: '#86EFAC' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                          <path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                        </svg>
                        <span className="text-sm font-medium" style={{ color: '#86EFAC' }}>Invitation link created!</span>
                        <button
                          type="button"
                          onClick={async () => {
                            await safeCopy(createdLink);
                            setLinkCopiedAgain(true);
                            setTimeout(() => setLinkCopiedAgain(false), 2000);
                          }}
                          className="ml-auto text-xs px-3 py-1 rounded-lg transition-all"
                          style={{ background: 'rgba(255,255,255,0.06)', color: linkCopiedAgain ? '#86EFAC' : 'var(--text-muted)' }}
                        >
                          {linkCopiedAgain ? '✓ Copied' : 'Copy link'}
                        </button>
                      </div>
                      <p className="text-[10px] mb-2" style={{ color: 'rgba(134,239,172,0.5)' }}>
                        Invite sent to <strong style={{ color: '#86EFAC' }}>{createdLinkEmail}</strong> as {inviteRole} · Link expires in 7 days
                      </p>
                      <p className="text-xs break-all font-mono" style={{ color: 'rgba(134,239,172,0.7)' }}>
                        {createdLink}
                      </p>
                      <p className="text-[10px] mt-2" style={{ color: 'rgba(134,239,172,0.45)' }}>
                        Share this link with your colleague — they can use it to join the project directly.
                      </p>
                    </div>
                  )}

                  {inviteError && <p className="text-xs" style={{ color: '#F87171' }}>{inviteError}</p>}
                  {inviteSuccess && <p className="text-xs" style={{ color: '#86EFAC' }}>{inviteSuccess}</p>}
                </div>
              </div>
            )}

            {/* Members list */}
            <div className="card overflow-hidden mb-6">
              <div className="px-5 py-4 border-b" style={{ borderColor: 'rgba(255,255,255,0.06)' }}>
                <h2 className="text-sm font-semibold" style={{ color: 'var(--text)' }}>
                  Members ({members.length})
                </h2>
              </div>

              {members.length === 0 ? (
                <div className="p-8 text-center">
                  <p className="text-sm" style={{ color: 'var(--text-muted)' }}>No members yet</p>
                </div>
              ) : (
                <div className="divide-y" style={{ borderColor: 'rgba(255,255,255,0.04)' }}>
                  {members.map(m => {
                    const isMe = m.user.id === user?.id;
                    const canEdit = isAdmin && !isMe;

                    return (
                      <div key={m.id}
                           className="flex items-center gap-4 px-5 py-4 hover:bg-white/[0.02] transition-colors">

                        <Avatar name={m.user.name} src={m.user.avatarUrl} size="md" />

                        <div className="flex-1 min-w-0">
                          <div className="flex items-center gap-2">
                            <span className="text-sm font-medium" style={{ color: 'var(--text)' }}>
                              {m.user.name}
                              {isMe && <span className="ml-1.5 text-[10px]" style={{ color: 'var(--text-subtle)' }}>(you)</span>}
                            </span>
                          </div>
                          <p className="text-xs truncate" style={{ color: 'var(--text-muted)' }}>{m.user.email}</p>
                        </div>

                        <span className="text-xs hidden sm:block" style={{ color: 'var(--text-subtle)' }}>
                          {new Date(m.joinedAt).toLocaleDateString('en-US', { month: 'short', year: 'numeric' })}
                        </span>

                        {editingRoleId === m.id ? (
                          <div className="flex items-center gap-2 shrink-0">
                            <select
                              className="input text-xs py-1.5"
                              value={editingRole}
                              onChange={e => setEditingRole(e.target.value)}
                              autoFocus
                            >
                              {Object.entries(ROLE_LABELS).map(([v, l]) => (
                                <option key={v} value={v}>{l}</option>
                              ))}
                            </select>
                            <button onClick={() => handleChangeRole(m.id)} disabled={updatingRole} className="btn btn-primary btn-sm px-2" title="Save">
                              <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                              </svg>
                            </button>
                            <button onClick={() => setEditingRoleId(null)} className="btn btn-secondary btn-sm px-2" title="Cancel">
                              <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
                              </svg>
                            </button>
                          </div>
                        ) : (
                          <div className="flex items-center gap-2 shrink-0">
                            <span className={`badge ${ROLE_COLORS[m.role] ?? 'badge-muted'}`}>
                              {ROLE_LABELS[m.role] ?? m.role}
                            </span>
                            {canEdit && (
                              <button
                                onClick={() => { setEditingRoleId(m.id); setEditingRole(m.role); }}
                                className="btn btn-secondary btn-sm"
                                title="Change role"
                              >
                                <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                  <path strokeLinecap="round" strokeLinejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125" />
                                </svg>
                              </button>
                            )}
                            {isAdmin && !isMe && (
                              <button
                                onClick={() => setConfirmRemove({ id: m.user.id, name: m.user.name })}
                                className="btn btn-danger btn-sm"
                                title="Remove from project"
                              >
                                <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                  <path strokeLinecap="round" strokeLinejoin="round" d="M22 12h-4l-3 9L9 3l-3 9H2" />
                                </svg>
                              </button>
                            )}
                          </div>
                        )}
                      </div>
                    );
                  })}
                </div>
              )}
            </div>

            {/* Pending invitations */}
            {canManage && (
              <div className="card overflow-hidden">
                <div className="px-5 py-4 border-b flex items-center justify-between" style={{ borderColor: 'rgba(255,255,255,0.06)' }}>
                  <h2 className="text-sm font-semibold" style={{ color: 'var(--text)' }}>
                    Pending invitations
                  </h2>
                  <span className="text-xs px-2 py-0.5 rounded-full"
                        style={{ background: 'rgba(255,255,255,0.05)', color: 'var(--text-muted)' }}>
                    {pendingInvites.length}
                  </span>
                </div>

                {pendingInvites.length === 0 ? (
                  <div className="p-8 text-center">
                    <p className="text-xs" style={{ color: 'var(--text-subtle)' }}>No pending invitations</p>
                  </div>
                ) : (
                  <div className="divide-y" style={{ borderColor: 'rgba(255,255,255,0.04)' }}>
                    {pendingInvites.map(inv => (
                      <div key={inv.id}
                           className="flex items-center gap-4 px-5 py-4">
                        <div className="w-9 h-9 rounded-full flex items-center justify-center shrink-0"
                             style={{ background: 'rgba(99,102,241,0.08)' }}>
                          <svg className="w-4 h-4" style={{ color: '#818CF8' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
                            <path strokeLinecap="round" strokeLinejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" />
                          </svg>
                        </div>

                        <div className="flex-1 min-w-0">
                          <div className="flex items-center gap-2">
                            <span className="text-sm font-medium" style={{ color: 'var(--text)' }}>{inv.email}</span>
                            <span className={`badge ${ROLE_COLORS[inv.role] ?? 'badge-muted'}`}>
                              {ROLE_LABELS[inv.role] ?? inv.role}
                            </span>
                          </div>
                          <div className="flex items-center gap-3 mt-0.5 text-xs" style={{ color: 'var(--text-subtle)' }}>
                            <span>Sent {new Date(inv.createdAt).toLocaleDateString()}</span>
                            <span>·</span>
                            <span>Expires {new Date(inv.expiresAt).toLocaleDateString()}</span>
                          </div>
                        </div>

                        <div className="flex items-center gap-1.5 shrink-0">
                          <button
                            onClick={() => handleCopyLink(inv)}
                            className="btn btn-secondary btn-sm"
                            title="Copy invite link"
                          >
                            {copiedInviteId === inv.id ? (
                              <svg className="w-3.5 h-3.5" style={{ color: '#86EFAC' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
                              </svg>
                            ) : (
                              <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244" />
                              </svg>
                            )}
                          </button>
                          <button
                            onClick={() => handleRevoke(inv.id)}
                            disabled={revokingId === inv.id}
                            className="btn btn-danger btn-sm"
                            title="Revoke invitation"
                          >
                            {revokingId === inv.id ? '…' : (
                              <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                                <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
                              </svg>
                            )}
                          </button>
                        </div>
                      </div>
                    ))}
                  </div>
                )}

                {pendingInvites.length > 0 && (
                  <div className="px-5 py-3 border-t" style={{ borderColor: 'rgba(255,255,255,0.04)' }}>
                    <p className="text-xs" style={{ color: 'var(--text-subtle)' }}>
                      Invitation links expire after 7 days. Copy the link and send it manually, or ask the recipient to check their email.
                    </p>
                  </div>
                )}
              </div>
            )}
          </div>
        )}
      </div>

      {/* Share modal */}
      {sharingAssetId && (
        <ShareModal
          assetId={sharingAssetId}
          onClose={() => setSharingAssetId(null)}
        />
      )}

      {/* Delete asset confirm modal */}
      {confirmDelete && (
        <div className="fixed inset-0 z-50 flex items-center justify-center"
             style={{ background: 'rgba(0,0,0,0.7)' }}>
          <div className="card p-6 max-w-sm w-full mx-4 animate-scale-in">
            <div className="flex items-center gap-3 mb-4">
              <div className="w-10 h-10 rounded-full flex items-center justify-center shrink-0"
                   style={{ background: 'rgba(248,113,113,0.15)' }}>
                <svg className="w-5 h-5" style={{ color: '#F87171' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
                </svg>
              </div>
              <div>
                <h3 className="text-base font-semibold" style={{ color: 'var(--text)' }}>Delete video?</h3>
                <p className="text-xs mt-0.5 truncate max-w-[220px]" style={{ color: 'var(--text-muted)' }}>
                  "{confirmDelete.title}"
                </p>
              </div>
            </div>
            <p className="text-sm mb-5" style={{ color: 'var(--text-muted)' }}>
              This will permanently delete the video file, thumbnail, and all HLS segments. This action cannot be undone.
            </p>
            <div className="flex gap-3 justify-end">
              <button onClick={() => setConfirmDelete(null)} disabled={!!deletingId} className="btn btn-secondary btn-md">Cancel</button>
              <button onClick={confirmDeleteAsset} disabled={!!deletingId} className="btn btn-danger btn-md">
                {deletingId === confirmDelete.id ? 'Deleting…' : 'Delete video'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Remove member confirm modal */}
      {confirmRemove && (
        <div className="fixed inset-0 z-50 flex items-center justify-center"
             style={{ background: 'rgba(0,0,0,0.7)' }}>
          <div className="card p-6 max-w-sm w-full mx-4 animate-scale-in">
            <h3 className="text-base font-semibold mb-2" style={{ color: 'var(--text)' }}>
              Remove {confirmRemove.name}?
            </h3>
            <p className="text-sm mb-5" style={{ color: 'var(--text-muted)' }}>
              They'll lose access to this project and all its videos. They can rejoin if invited again.
            </p>
            <div className="flex gap-3 justify-end">
              <button onClick={() => setConfirmRemove(null)} className="btn btn-secondary btn-md">Cancel</button>
              <button onClick={handleRemoveMember} disabled={removing} className="btn btn-danger btn-md">
                {removing ? 'Removing…' : 'Remove'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Delete project confirm modal */}
      {confirmDeleteProject && (
        <div className="fixed inset-0 z-50 flex items-center justify-center"
             style={{ background: 'rgba(0,0,0,0.7)' }}>
          <div className="card p-6 max-w-sm w-full mx-4 animate-scale-in">
            <div className="flex items-center gap-3 mb-4">
              <div className="w-10 h-10 rounded-full flex items-center justify-center shrink-0"
                   style={{ background: 'rgba(248,113,113,0.15)' }}>
                <svg className="w-5 h-5" style={{ color: '#F87171' }} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
                  <path strokeLinecap="round" strokeLinejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
                </svg>
              </div>
              <div>
                <h3 className="text-base font-semibold" style={{ color: 'var(--text)' }}>
                  Delete "{project?.name}"?
                </h3>
                <p className="text-xs mt-0.5" style={{ color: '#F87171' }}>
                  {assets.length} video{assets.length !== 1 ? 's' : ''} will be permanently deleted
                </p>
              </div>
            </div>
            <p className="text-sm mb-5" style={{ color: 'var(--text-muted)' }}>
              This will permanently delete the project, all videos, comments, and assets. This action cannot be undone.
            </p>
            <div className="flex gap-3 justify-end">
              <button
                onClick={() => setConfirmDeleteProject(false)}
                disabled={deletingProject}
                className="btn btn-secondary btn-md"
              >
                Cancel
              </button>
              <button
                onClick={handleDeleteProject}
                disabled={deletingProject}
                className="btn btn-danger btn-md"
              >
                {deletingProject ? 'Deleting…' : 'Delete Project'}
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
